﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ColorClusteringSOM.Util;

namespace ColorClusteringSOM
{
    /// <summary>
    /// Trida, ktera se stara o klasifikaci dat
    /// </summary>
    class Classification
    {

        private Network trainedNetwork;

        private Neuron[] winners = null;

        public Network TrainedNetwork
        {
            get { return trainedNetwork; }
            set { trainedNetwork = value; }
        }

        public Neuron[] Winners { get { return this.winners; } }

        public Classification(Network trainedNetwork)
        {
            this.trainedNetwork = trainedNetwork;
            Console.WriteLine("Spusteni klasifikace...");
        }


        /// <summary>
        /// Jednoducha jen tridi pole
        /// Plni pole  Neuron[] Winners
        /// </summary>
        /// <param name="data"></param>
        public Neuron[] SimpleClassification(List<int[]> data)
        {

            winners = new Neuron[data.Count];

            int count = 0;
            foreach (int[] array in data)
            {
                winners[count] = WinnerOfClassification(array);

                count += 1;
            }
            return winners;
        }



        /// <summary>
        /// Vrati winnera pro jeden dany vstup
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public Neuron WinnerOfClassification(int[] data)
        {

            Neuron winner = null;
            double euklid = double.MaxValue;

            double[] input = NormalizeInput(data);

            for (int i = 0; i < trainedNetwork.Rows; i++)
            {
                for (int j = 0; j < trainedNetwork.Cols; j++)
                {
                    Neuron actualNeuron = trainedNetwork[i, j];

                    //euklidovska vzdalenost : vstup a vahy neuronu
                    double actualEuklid = EuklidDistance(input, actualNeuron.Weights);
                    if (actualEuklid < euklid) // (actualEuklid <= euklid) ??? 
                    {
                        winner = actualNeuron;
                        euklid = actualEuklid;

                    }
                }
            }
            return winner;

        }

        protected double EuklidDistance(double[] input, double[] weights)
        {

            double euDistance = 0;
            double pom = 0;

            for (int i = 0; i < input.Length; i++)
            {
                pom = input[i] - weights[i];
                euDistance += pom * pom;
            }

            return Math.Sqrt(euDistance);


        }

        /// <summary>
        ///  Metoda co normalizuje cele pole a prevede ho double[]
        ///  TODO: Metoda je presunuta do Helper, takze celou tridu prizpusobit
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        protected double[] NormalizeInput(int[] data)
        {
            double[] Dinput = new double[data.Length];

            for (int i = 0; i < data.Length; i++)
            {
                double input = data[i];
                Dinput[i] = (input / 256);
            }

            return Dinput;
        }



        public Network FIRSTClassificateIntruders(String nameFile)
        {
            double[,] valMatrix = Reader.IntruderValidationReader(nameFile); // validacni sit

            double minDistance = Double.MaxValue;
            Neuron actualNeuron = null;
            Neuron winner = null;

            //------------------------
            double[] input = new double[42];
            int pomAttack = -1;
            //------------------------

            int att = 0;
            int NOatt = 0;
            for (int valRow = 0; valRow < 6890; valRow++)// prochazeni cele kolekce
            {
                pomAttack = Int32.MaxValue;


                for (int i = 0; i <= 41; i++) // pole pro vstupy
                {
                    if (i < 41)
                    {
                        input[i] = valMatrix[valRow, i];

                    }
                    else
                    {
                        pomAttack = (int)valMatrix[valRow, i];
                    }
                }
                if (pomAttack == 1)
                {
                    att++;
                }
                if (pomAttack == 0)
                {
                    NOatt++;
                }

                minDistance = Double.MaxValue;
                //prochazeni somu
                for (int i = 0; i < trainedNetwork.Rows; i++)
                {
                    for (int j = 0; j < trainedNetwork.Cols; j++)
                    {
                        actualNeuron = trainedNetwork[i, j];

                        double distance = EuklidDistance(actualNeuron.Weights, input);

                        if (distance < minDistance)
                        {
                            winner = actualNeuron;
                            minDistance = distance;

                        }

                    }
                }
                if (pomAttack == 1)
                {
                    winner.Attack();
                }
                if (pomAttack == 0)
                {
                    winner.NOAttack();
                }
                winner = null;

            }
            return trainedNetwork;
        }


        public String SECONDClassificateIntruders(String nameFile)
        {
            ///////////////////////////
            int goodAttack = 0;     // dobra klasifikace utoku
            int goodNoAttack = 0;   // dobra klasifikace neutoku
            int badAttack = 0;      //spatna klasifikace utoku
            int badNoAttack = 0;    //spatna klasifikace neutoku

            ///////////////////////////

            double[,] valMatrix = Reader.IntruderValidationReader(nameFile); // validacni sit

            double minDistance = Double.MaxValue;
            Neuron actualNeuron = null;
            Neuron winner = null;

            //------------------------
            double[] input = new double[42];
            int pomAttack = -1;
            //------------------------


            for (int valRow = 0; valRow < 6890; valRow++)// prochazeni cele kolekce
            {
                pomAttack = Int32.MaxValue;


                for (int i = 0; i <= 41; i++) // pole pro vstupy
                {
                    if (i < 41)
                    {
                        input[i] = valMatrix[valRow, i];

                    }
                    else
                    {
                        pomAttack = (int)valMatrix[valRow, i];
                    }
                }


                minDistance = Double.MaxValue;
                //prochazeni somu
                for (int i = 0; i < trainedNetwork.Rows; i++)
                {
                    for (int j = 0; j < trainedNetwork.Cols; j++)
                    {
                        actualNeuron = trainedNetwork[i, j];

                        double distance = EuklidDistance(actualNeuron.Weights, input);

                        if (distance < minDistance)
                        {
                            winner = actualNeuron;
                            minDistance = distance;

                        }

                    }
                }


                if (winner.NeuronType == pomAttack && pomAttack == 1)
                {
                    goodAttack++;
                }
                else if (winner.NeuronType == pomAttack & pomAttack == 0)
                {
                    goodNoAttack++;
                }
                else if (winner.NeuronType != pomAttack && pomAttack == 1 && winner.NeuronType == 0)
                {
                    badAttack++;
                }
                else if (winner.NeuronType != pomAttack && pomAttack == 0 && winner.NeuronType == 1)
                {
                    badNoAttack++;
                }




                winner = null;

            }
            double totalGood = ((((double)goodAttack + (double)goodNoAttack) / (double)6890) * (double)100);
            double totalBad = ((((double)badAttack + (double)badNoAttack) / (double)6890) * (double)100);
            StringBuilder s = new StringBuilder();
            //s.Append("\nGood classification of attack:\t" + goodAttack + "\n");
            //s.Append("Good classification of NONattack:\t" + goodNoAttack + "\n");
            //s.Append("Bad classification of attack:\t" + badAttack + "\n");
            //s.Append("Bad classification of NONattack:\t" + badNoAttack + "\n\n");
            //s.Append("Total good classification:" + (goodAttack + goodNoAttack) + "\t" + (int)totalGood + "%\n");
            //s.Append("Total Bad classification:" + (badAttack + badNoAttack) + "\t" + (int)totalBad + "%\n");


            s.Append(" "+goodAttack+" ");
            s.Append(goodNoAttack + " ");
            s.Append(badAttack + " ");
            s.Append(badNoAttack + " ");
            s.Append((goodAttack + goodNoAttack) + " ");
            s.Append(totalGood + " ");
            s.Append((badAttack + badNoAttack)+" ");
            s.Append(totalBad + " ");

            return s.ToString();
        }



        public String SECONDClassificateIntrudersKMEANS(String nameFile, List<NeuronRough> leaders)
        {
            ///////////////////////////
            int goodAttack = 0;     // dobra klasifikace utoku
            int goodNoAttack = 0;   // dobra klasifikace neutoku
            int badAttack = 0;      //spatna klasifikace utoku
            int badNoAttack = 0;    //spatna klasifikace neutoku

            ///////////////////////////

            double[,] valMatrix = Reader.IntruderValidationReader(nameFile); // validacni sit

            double minDistance = Double.MaxValue;
            Neuron actualNeuron = null;
            Neuron winner = null;

            //------------------------
            double[] input = new double[42];
            int pomAttack = -1;
            //------------------------


            for (int valRow = 0; valRow < 6890; valRow++)// prochazeni cele kolekce
            {
                pomAttack = Int32.MaxValue;


                for (int i = 0; i <= 41; i++) // pole pro vstupy
                {
                    if (i < 41)
                    {
                        input[i] = valMatrix[valRow, i];

                    }
                    else
                    {
                        pomAttack = (int)valMatrix[valRow, i];
                    }
                }
                

                minDistance = Double.MaxValue;
                NeuronRough pomLeader = null; // pro zapamatovani ktery leader to byl

                foreach (NeuronRough leader in leaders)
                {
                    foreach (NeuronRough neuron in leader.LowerAppNeuronsArray)
                    {
                        double distance = EuklidDistance(neuron.Neuron.Weights, input);

                        if (distance < minDistance)
                        {
                            winner = actualNeuron;
                            pomLeader = leader;
                            minDistance = distance;

                        }
                    }
                }
                              

                int neuronType = 0;
                if (pomLeader.LeaderName == "A")
                {
                    neuronType = 1;
                }

                if (neuronType == pomAttack && pomAttack == 1)
                {
                    goodAttack++;
                }
                else if (neuronType == pomAttack & pomAttack == 0)
                {
                    goodNoAttack++;
                }
                else if (neuronType != pomAttack && pomAttack == 1 && neuronType == 0)
                {
                    badAttack++;
                }
                else if (neuronType != pomAttack && pomAttack == 0 && neuronType == 1)
                {
                    badNoAttack++;
                }




                winner = null;

            }
            double totalGood = ((((double)goodAttack + (double)goodNoAttack) / (double)6890) * (double)100);
            double totalBad = ((((double)badAttack + (double)badNoAttack) / (double)6890) * (double)100);
            StringBuilder s = new StringBuilder();
            //s.Append("\nGood classification of attack:\t" + goodAttack + "\n");
            //s.Append("Good classification of NONattack:\t" + goodNoAttack + "\n");
            //s.Append("Bad classification of attack:\t" + badAttack + "\n");
            //s.Append("Bad classification of NONattack:\t" + badNoAttack + "\n\n");
            //s.Append("Total good classification:" + (goodAttack + goodNoAttack) + "\t" + (int)totalGood + "%\n");
            //s.Append("Total Bad classification:" + (badAttack + badNoAttack) + "\t" + (int)totalBad + "%\n");


            s.Append(" " + goodAttack + " ");
            s.Append(goodNoAttack + " ");
            s.Append(badAttack + " ");
            s.Append(badNoAttack + " ");
            s.Append((goodAttack + goodNoAttack) + " ");
            s.Append(totalGood + " ");
            s.Append((badAttack + badNoAttack) + " ");
            s.Append(totalBad + " ");

            return s.ToString();
        }

        public String SECONDClassificateIntrudersROUGH(String nameFile, List<NeuronRough> clusters)
        {

            ///////////////////////////
            int goodAttack = 0;     // dobra klasifikace utoku
            int goodNoAttack = 0;   // dobra klasifikace neutoku
            int unableDistingAttack = 0;      //spatna klasifikace utoku
            int unableDistingNoAttack = 0;    //spatna klasifikace neutoku

            ///////////////////////////

            double[,] valMatrix = Reader.IntruderValidationReader(nameFile); // validacni sit

            double minDistance = Double.MaxValue;
            //Neuron actualNeuron = null;
            

            //------------------------
            double[] input = new double[42];
            int InputValueAttack = -1;
            //------------------------


            for (int valRow = 0; valRow < 6890; valRow++)// prochazeni cele kolekce
            {
                InputValueAttack = Int32.MaxValue;


                for (int i = 0; i <= 41; i++) // pole pro vstupy
                {
                    if (i < 41)
                    {
                        input[i] = valMatrix[valRow, i];

                    }
                    else
                    {
                        InputValueAttack = (int)valMatrix[valRow, i];
                    }
                }


                minDistance = Double.MaxValue;
                NeuronRough pomLeaderLower = null; // pro zapamatovani ktery leader to byl
                NeuronRough pomLeaderUpper = null;

                NeuronRough winnerLower = null;
                NeuronRough winnerUpper = null;


                double minDistanceLOWER = Double.MaxValue;
                double minDistanceUPPER = Double.MaxValue;

                foreach (NeuronRough leader in clusters)
                {
                    
                    foreach (NeuronRough neuron in leader.LowerAppNeuronsArray)
                    {
                        double distance = EuklidDistance(neuron.Neuron.Weights, input);

                        if (distance < minDistanceLOWER)
                        {
                            winnerLower = neuron;
                            pomLeaderLower = leader;
                            minDistanceLOWER = distance;

                        }
                    }
                   
                    foreach (NeuronRough upper in leader.UpperAppNeuronsArray)
                    {
                        double distance = EuklidDistance(upper.Neuron.Weights, input);
                        if (distance < minDistanceUPPER)
                        {
                            winnerUpper = upper;
                            pomLeaderUpper = leader;
                            minDistanceUPPER = distance;

                        }
                    }
                    
                }

                NeuronRough winner = null;
                NeuronRough finallLeader = null;
                if (minDistanceLOWER > minDistanceUPPER)
                {
                    winner = winnerUpper;
                    finallLeader = pomLeaderUpper;

                }
                if (minDistanceLOWER < minDistanceUPPER)
	            {
                    winner = winnerLower;
                    finallLeader = pomLeaderLower;
	            }



                if (winner.Neuron.AttackCounter() > 0 && InputValueAttack == 1 && finallLeader.LeaderName == "A")
                {
                    goodAttack++;
                }
                else if (winner.Neuron.AttackCounter() < 0 && InputValueAttack == 0 &&finallLeader.LeaderName == "N")
                {
                    goodNoAttack++;
                }
                else if (InputValueAttack == 1 && winner.Neuron.NoEntry== true )
                {
                    unableDistingAttack++;
                }
                else if (InputValueAttack == 0 && winner.Neuron.NoEntry == true)
                {
                    unableDistingNoAttack++;
                }







                winnerLower = null;

            }
            double totalGood = ((((double)goodAttack + (double)goodNoAttack) / (double)6890) * (double)100);
            double totalUnableDistinction = ((((double)unableDistingAttack + (double)unableDistingNoAttack) / (double)6890) * (double)100);
            StringBuilder s = new StringBuilder();
            //s.Append("\nGood classification of attack:\t" + goodAttack + "\n");
            //s.Append("Good classification of NONattack:\t" + goodNoAttack + "\n");
            //s.Append("Unable classification of attack:\t" + unableDistingAttack + "\n");
            //s.Append("Unable classification of NONattack:\t" + unableDistingNoAttack + "\n\n");
            //s.Append("Total good classification:" + (goodAttack + goodNoAttack) + "\t" + (int)totalGood + "%\n");
            //s.Append("Total Unable classification:" + (unableDistingAttack + unableDistingNoAttack) + "\t" + (int)totalUnableDistinction + "%\n");


            s.Append(" & " + goodAttack + " & ");
            s.Append(goodNoAttack + " & ");
            s.Append(unableDistingAttack + " & ");
            s.Append(unableDistingNoAttack + " & ");
            s.Append((goodAttack + goodNoAttack) + " & ");
            s.Append(Math.Round(totalGood,2) + " & ");
            s.Append((unableDistingAttack + unableDistingNoAttack) + " & ");
            s.Append(Math.Round(totalUnableDistinction,2) + " ");

            return s.ToString();
        }
    }
}
